/*
 * This script is code example to learn how to use "underscore.js".
 * To test code below, both underscore.js & jQuery are required.
 *
 * Author: Kensaku KOMATSU
 * Last Updated: 2011/6/4(sat)
 * Base Document => http://documentcloud.github.com/underscore/
 */

/**
 * Collections
 *
 */

/////////////////////////////////////////////////
// each
// _.each(list, iterator, [context]) Alias: forEach
//
// list内の要素(element)に対し、iteratorで定義された関数処理を
// 繰り返し実行する。listがArrayの場合、iteratorに渡される
// 引数は(element, index, list), objectの場合は、
// (value, key, list)となる。
/////////////////////////////////////////////////

// listがArrayの場合
(function(){
  var sum = [0, 0];
  _.each([2,4,6], function(num, idx, list){
    sum[0] += num * this.a;
    sum[1] += idx;
  }, {a:3});
  console.log(sum);
  // #=> [36,3]
})();
// listがObjectの場合
(function(){
  var sum = [0, ""];
  _.each({a:2,b:4,c:6}, function(val, key, list){
    sum[0] += val * this.a;
    sum[1] += key;
  }, {a:3});
  console.log(sum);
  // #=> [36,"abc"]
})();


/////////////////////////////////////////////////
// map
// _.map(list, iterator, [context])
//
// list内の各要素にiteratorで定義された関数処理に従い、
// 新たなArrayを生成する。listがArrayの場合、iteratorに
// 渡される引数は、listの各要素値、Objectの場合は、
// （value, key)となる。
/////////////////////////////////////////////////

// listがArrayの場合
(function(){
  var arr = _.map([2,4,6], function(num){
    return num*this.a;
  }, {a:3});
  console.log(arr);
  // #=> [6,12,18]
})();
// listがObjectの場合
(function(){
  var arr = _.map({a:2,b:4,c:6}, function(val, key){
    return val * this.a;
  }, {a:3});
  console.log(arr);
  // #=> [6,12,18]
})();

/////////////////////////////////////////////////
// reduce
// _.reduce(list, iterator, memo, [context]) Aliases: inject, foldl
//
// listで与えられたArrayもしくはObjectから、iteratorで渡された関数処理に
// 従い、一つの値を得る。memoは、生成する値の初期値。
/////////////////////////////////////////////////

// listがArrayの場合
(function(){
  var ret = _.reduce([2,4,6], function(memo, num){
    return memo + num*this.a;
  }, 1, {a:3});
  console.log(ret);
  // #=> 37
})();
// listがObjectの場合
(function(){
  var ret = _.reduce({a:2,b:4,c:6}, function(memo, val, key){
    return memo + val * this.a;
  }, 1, {a:3});
  console.log(ret);
  // #=> 37
})();

/////////////////////////////////////////////////
// reduceRight
// _.reduceRight(list, iterator, memo, [context]) Alias: foldr
//
// reduceがlist内の要素を左から処理するのに対し、reduceRightでは
// 右から処理する。低速動作となるため、あまり有用ではない。
/////////////////////////////////////////////////

// listがArrayの場合
(function(){
  var ret = _.reduceRight([2,4,6], function(memo, num){
    return memo.concat(num * this.a);
  }, [], {a:3});
  console.log(ret);
  // #=> [18, 12, 6]
})();
// listがObjectの場合
(function(){
  var ret = _.reduceRight({a:2,b:4,c:6}, function(memo, val, key){
    return memo.concat(val * this.a);
  }, [], {a:3});
  console.log(ret);
  // #=> [18, 12, 6]
})();

/////////////////////////////////////////////////
// detect
// _.detect(list, iterator, [context])
//
// list内の各要素に対して、iteratorで定義された処理を実行し、
// 最初にtrueとなる要素を返す。trueが検出された時点で、iterator
// の処理は終了となり、listの全要素を処理することはない。
/////////////////////////////////////////////////

// listがArrayの場合
console.log("_detect");
(function(){
  var ret = _.detect([2,4,6], function(num){
    return num*this.a == 12;
  }, {a:3});
  console.log(ret);
  // #=> 4
})();
// listがObjectの場合
console.log("_detect");
(function(){
  var ret = _.detect({a:2,b:4,c:6}, function(val, key){
    return val*this.a == 12;
  }, {a:3});
  console.log(ret);
  // #=> 4
})();

/////////////////////////////////////////////////
// select
// _.select(list, iterator, [context]) Alias: filter
//
// list内の要素に対し、iteratorで定義された処理に対し、
// trueを返す要素のみの配列を返す。
/////////////////////////////////////////////////

// listがArrayの場合
console.log("_select");
(function(){
  var ret = _.select([2,3,4,5,6], function(num){
    return num % this.a == 0;
  }, {a:2});
  console.log(ret);
  // #=> [2, 4, 6]
})();
// listがObjectの場合
console.log("_select");
(function(){
  var ret = _.select({a:2,b:3,c:4}, function(val, key){
    return val % this.a == 0;
  }, {a:2});
  console.log(ret);
  // #=> [2, 4]
})();

/////////////////////////////////////////////////
// reject
// _.reject(list, iterator, [context]) Alias: filter
//
// _.selectと逆の動作。list内の要素に対し、iteratorで定義された
// 処理を実行し、true以外の要素の配列を返す。
/////////////////////////////////////////////////

// listがArrayの場合
console.log("_reject");
(function(){
  var ret = _.reject([2,3,4,5,6], function(num){
    return num % this.a == 0;
  }, {a:2});
  console.log(ret);
  // #=> [3,5]
})();
// listがObjectの場合
console.log("_reject");
(function(){
  var ret = _.reject({a:2,b:3,c:4}, function(val, key){
    return val % this.a == 0;
  }, {a:2});
  console.log(ret);
  // #=> [3]
})();

/////////////////////////////////////////////////
// all
// _.all(list, iterator, [context]) Alias: every
//
// list内の要素に対し、iteratorを実行。全要素が
// trueの時に trueを返す。それ以外はfalse
/////////////////////////////////////////////////

// listがArrayの場合
console.log("_all");
(function(){
  var ret = _.all([2,3,4,5,6], function(num){
    return num % this.a == 0;
  }, {a:2});
  console.log(ret);
  // #=> false
})();
// listがObjectの場合
console.log("_all");
(function(){
  var ret = _.all({a:2,b:3,c:4}, function(val, key){
    return val >= this.a;
  }, {a:2});
  console.log(ret);
  // #=> true
})();

/////////////////////////////////////////////////
// any
// _.any(list, iterator, [context]) Alias: some
//
// list内の要素に対し、iteratorを実行。一つでも
// trueがあれば、trueを返す。trueを見つけた時点で
// iterationは終了する。
/////////////////////////////////////////////////

// listがArrayの場合
console.log("_any");
(function(){
  var ret = _.any([2,3,4,5,6], function(num){
    return num % this.a == 0;
  }, {a:2});
  console.log(ret);
  // #=> true
})();
// listがObjectの場合
console.log("_any");
(function(){
  var ret = _.any({a:2,b:3,c:4}, function(val, key){
    return val < this.a;
  }, {a:2});
  console.log(ret);
  // #=> false
})();

/////////////////////////////////////////////////
// include
// _.include(list, value) Alias: contains
//
// list内にvalueがあれば、trueを返す(===で評価するため、
// 型も含めチェック）。listがArrayの場合、内部的には
// indexOfを用いている。
/////////////////////////////////////////////////

// listがArrayの場合
console.log("_include");
(function(){
  var ret = _.include([2,3,4,5,6], 3);
  console.log(ret);
  // #=> true
})();
// listがObjectの場合
console.log("_include");
(function(){
  var ret = _.include({a:2,b:3,c:4}, 3);
  console.log(ret);
  // #=> true
})();

/////////////////////////////////////////////////
// invoke
// _.invoke(list, methodName, [*arguments])
//
// list内の要素に対し、methodNameで与えられる関数を
// 実行する。argumentsは、methodNameの関数呼び出し。
/////////////////////////////////////////////////

// case 1
console.log("_invoke");
(function(){
  var ret = _.invoke([[3,2], [6,4]], "sort");
  console.log(ret);
  // #=> [[2,3], [4,6]]
})();
// case 2
console.log("_invoke");
(function(){
  var ret = _.invoke([[12,3],[21, 5]], "sort", function(a,b){return a-b});
  console.log(ret);
  // #=> [[3,12], [5,21]]
})();

/////////////////////////////////////////////////
// pluck
// _.pluck(list, propertyName)
//
// listに対し、propertyNameで指定された値の配列を
// 返す
/////////////////////////////////////////////////

//
console.log("_pluck");
(function(){
  var ret = _.pluck([{a:1, b:2}, {a:5, b:7}], "a");
  console.log(ret);
  // #=> [1,5]
})();

/////////////////////////////////////////////////
// max
// _.max(list, [iterator], [context])
//
// list中の最大値を返す。iteratorにより、判定基準を
// 指定可能
/////////////////////////////////////////////////

// case wo/ iterator
console.log("_max");
(function(){
  var ret = _.max([2,6,5]);
  console.log(ret);
  // #=> 6
})();
// case 2/ iterator
console.log("_max");
(function(){
  var ret = _.max([{name:"hoge", price:1000},{name:"fuga", price:500}], function(obj){return obj.price;});
  console.log(ret);
  // #=> {name:"hoge", price:1000}
})();

/////////////////////////////////////////////////
// min
// _.min(list, [iterator], [context])
//
// list中の最大値を返す。iteratorにより、判定基準を
// 指定可能
/////////////////////////////////////////////////

// case wo/ iterator
console.log("_min");
(function(){
  var ret = _.min([2,6,5]);
  console.log(ret);
  // #=> 2
})();
// case 2/ iterator
console.log("_min");
(function(){
  var ret = _.min([{name:"hoge", price:1000},{name:"fuga", price:500}], function(obj){return obj.price;});
  console.log(ret);
  // #=> {name:"fuga", price:500}
})();

/////////////////////////////////////////////////
// sortBy
// _.sortBy(list, iterator, [context])
//
// listに対し、iteratorの演算結果でソートする
/////////////////////////////////////////////////

console.log("_sortBy");
(function(){
  var ret = _.sortBy([{name:"hoge", price:1000},{name:"fuga",price:500}], function(obj){return obj.price});
  console.log(ret);
  // #=> [{name:"fuga",price:500},{name:"hoge",price:1000}]
})();

/////////////////////////////////////////////////
// sortedIndex
// _.sortedIndex(list, value, [iterator])
//
// listの中で、valueが何番目に入るかを返す。ソートされた
// listのオーダーを保持するのに有効。iteratorが渡された
// 場合は、listのソート計算に用いられる。
/////////////////////////////////////////////////

console.log("_sortedIndex");
(function(){
  var ret = _.sortedIndex([10,20,30,40], 35);
  console.log(ret);
  // #=> 3
})();

/////////////////////////////////////////////////
// toArray
// _.toArray(list)
//
// listを配列に変換する。argumentsの変換に便利。
/////////////////////////////////////////////////

console.log("_toArray");
(function(){
  var ret = (function(){ return _.toArray(arguments); })(1,2,3);
  console.log(ret);
  // #=> [1,2,3]
})();

/////////////////////////////////////////////////
// size
// _.size(list)
//
// listの長さを返す。
/////////////////////////////////////////////////

console.log("_size");
(function(){
  var ret = _.size({a:1, b:2, c:1});
  console.log(ret);
  // #=> 3
})();

/**
 * Arrays
 *
 */
/////////////////////////////////////////////////
// first
// _.first(array, [n]) Alias: head
//
// arrayの最初の要素を返す。nを指定した場合、最初から
// n個までの配列を返す。
/////////////////////////////////////////////////

console.log("_.first");
(function(){
  var ret = _.first([1,2,3,4,5]);
  console.log(ret);
  // #=> 1
  ret = _.first([1,2,3,4,5], 2);
  console.log(ret);
  // #=> [1,2]
})();

/////////////////////////////////////////////////
// rest
// _.rest(array, [n]) Alias: tail
//
// arrayの残りの要素を返す。nを指定すると、n番目以降の
// 配列が返される。
/////////////////////////////////////////////////

console.log("_.rest");
(function(){
  var ret = _.rest([1,2,3,4,5]);
  console.log(ret);
  // #=> 1
  ret = _.rest([1,2,3,4,5], 2);
  console.log(ret);
  // #=> [1,2]
})();

/////////////////////////////////////////////////
// last
// _.last(array)
//
// arrayの最後の要素を返す。
/////////////////////////////////////////////////

console.log("_.last");
(function(){
  var ret = _.last([1,2,3,4,5]);
  console.log(ret);
  // #=> 5
})();

/////////////////////////////////////////////////
// compact
// _.compact(array)
//
// arrayから、偽になる値を削除する。false, null, 0, "",
// undefinedとNaNが偽である。
/////////////////////////////////////////////////

console.log("_.compact");
(function(){
  var ret = _.compact([1,false,2,0,4,undefined]);
  console.log(ret);
  // #=> [1,2,4]
})();

/////////////////////////////////////////////////
// flatten
// _.flatten(array)
//
// ネストされた array をフラットにする。
/////////////////////////////////////////////////

console.log("_.flatten");
(function(){
  var ret = _.flatten([1,[2],[3, [[4]]]]);
  console.log(ret);
  // #=> [1,2,3,4]
})();

/////////////////////////////////////////////////
// without
// _.without(array, [*value])
//
// arrayからvalueで指定した値を削除する。値のチェックに
// === を用いている。
/////////////////////////////////////////////////

console.log("_.without");
(function(){
  var ret = _.without([1,2,0,3,2,"1"], 0, 1);
  console.log(ret);
  // #=> [2,3,2,"1"]
})();

/////////////////////////////////////////////////
// uniq
// _.uniq(array, [isSorted])
//
// arrayから重複値を削除し、ユニークな値による配列を返す。
// arrayがsortされているばあい、isSortedをtrueに設定すると、
// 高速なアルゴリズムが適用される。
/////////////////////////////////////////////////

console.log("_.uniq");
(function(){
  var ret = _.uniq([2,4,1,4,0,2]);
  console.log(ret);
  // #=> [2,4,1,0]
})();

/////////////////////////////////////////////////
// intersect
// _.intersect(*arrays)
//
// arraysの積集合（共通の値）による配列を返す。
/////////////////////////////////////////////////

console.log("_.intersect");
(function(){
  var ret = _.intersect([1,2,3], [3,1,4]);
  console.log(ret);
  // #=> [1,3]
})();

/////////////////////////////////////////////////
// zip
// _.zip(*arrays)
//
// arraysのindex値が一致する値をマージする。
/////////////////////////////////////////////////

console.log("_.zip");
(function(){
  var ret = _.zip(['a', 'b', 'c'], [30,40,50],[true, false, true]);
  console.log(ret);
  // #=> [['a',30,true],['b',40,false],['c',50,true]]
})();

/////////////////////////////////////////////////
// indexOf
// _.indexOf(array, value, [isSorted])
//
// arrayの中のvalueの位置を返す。valueが存在しない場合は、
// -1 を返す。arrayがソートされている場合は、isSortedを
// trueにすると、高速な binary search(二分探索）を行う。
/////////////////////////////////////////////////

console.log("_.indexOf");
(function(){
  var ret = _.indexOf([1,4,2], 4)
  console.log(ret);
  // #=> 1
})();

/////////////////////////////////////////////////
// lastIndexOf
// _.lastIndexOf(array, value)
//
// array中で、valueが最後に出現するインデックス値を返す。
// 見つからない場合は -1を返す。
/////////////////////////////////////////////////

console.log("_.lastIndexOf");
(function(){
  var ret = _.lastIndexOf([1,4,2,4], 4)
  console.log(ret);
  // #=> 3
})();

/////////////////////////////////////////////////
// range
// _.range([start], stop, [step])
//
// 整数値による連続した配列を返す。eachやmapに用いるのに
// 便利である。start が省略された場合のdefaultは0, stepが
// 省略された場合のdefaultは1である。startからstopまで、
// step毎に増加もしくは減少する整数値を返す。
/////////////////////////////////////////////////

console.log("_.range");
(function(){
  console.log(_.range(10));
  // #=> [0,1,2,3,4,5,6,7,8,9]
  console.log(_.range(1,11));
  // #=> [1,2,3,4,5,6,7,8,9,10]
  console.log(_.range(0,30,5));
  // #=> [0,5,10,15,20,25]
  console.log(_.range(0,-10,-1));
  // #=> [0,-1,-2,-3,-4,-5,-6,-7,-8,-9]
})();



/**
 * Functions
 *
 */
/////////////////////////////////////////////////
// bind
// _.bind(function, object, [*arguments])
//
// functionをobjectにbindする。このとき、thisはobjectを指す。
// argumentsをbindすること(カリー化)も可能。
/////////////////////////////////////////////////
(function(){
  var func = function(item){return item + " : "+this.price+" Yen"}
  func = _.bind(func, {price: 100}, 'pen');
  console.log(func());
  // #=> 'pen : 100 Yen'
})();

//////////////////////////////////////////////////
// bindAll
// _.bindAll(object, [*methodNames])
//
// methodNameで指定されたobjectのメソッドに対し、thisをobjectにする。
// eventハンドラ内で、methodが用いられる場合に有用。mehotdNamesが指定
// されていないときは、object内のすべてのメソッドに適用される。
//////////////////////////////////////////////////
(function(){
  var buttonView0 = {
    label: 'never seen',
    onClick: function(){ $("#baRes").html('clicked: '+this.label); }
  }
  var buttonView1 = {
    label: 'underscore',
    onClick: function(){ $("#baRes").html('clicked: '+this.label); }
  }
  _.bindAll(buttonView1);
  $('#baBtn0').click(buttonView0.onClick);
  // #=> "undefined"
  $('#baBtn1').click(buttonView1.onClick);
  // #=> "underscore"
})();


//////////////////////////////////////////////////
// memoize
// _.memoize(function, [hashFunction])
//
// functionの計算結果をキャッシュする。演算時間の高速化に有効。
// hashFunctionが指定された場合は、結果をストアするハッシュキーの
// 生成（functionの引数がベース）に用いられる。デフォルトのhashFunction
// は、functionの第一引数をキーとして用いる動作をする。
//////////////////////////////////////////////////

(function(){
  var fibonacci = function(n) {
    return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
  };
  var fastFibonacci = _.memoize(fibonacci);

  console.log('### memoize ###');
  // common case
  var tmp = new Date().getTime();
  fibonacci(35); // #=> 619 msec on my MBA;-<
  console.log(new Date().getTime() - tmp);

  // rapid compute
  var tmp = new Date().getTime();
  fastFibonacci(35); // #=> 587 msec on my MBA;-)
  console.log(new Date().getTime() - tmp);
})();


//////////////////////////////////////////////////
// delay
// _.delay(function, wait, [*arguments])
//
// setTimeoutのように、waitミリ秒経過後に functionを実行する。
// argumentsを指定すると、functionの引数として与えられる。
//////////////////////////////////////////////////

(function(){
  console.log('### delay ###');
  var log = _.bind(console.log, console);
  _.delay(log, 2000, 'logged later');
  // #=> 'logged later' // 2秒後に表示される
})();

//////////////////////////////////////////////////
// defer
// _.defer(function)
//
// 現在のコールスタックがクリアになるまで、関数の実行を
// 延ばす。setTimeout()を0秒で用いる動作に似ている。
// コストのかかる処理をチャンク分割し、updateすることで、
// UIブロックを防ぐ際に有効
//////////////////////////////////////////////////

(function(){
  console.log('### defer ###');
  _.defer(function(){ console.log('deferred'); });
  console.log('executed');
  // #=> 'executed -> deferred'
})();

//////////////////////////////////////////////////
// throttle
// _.throttle(function, wait)
//
// 連続的に発生する処理を、waitミリ秒ごとに定期的に
// 実行するようにする。定期実行イベントを記述する際に
// 有効
//////////////////////////////////////////////////

(function(){
  console.log('### throttle ###');
  var throttled = _.throttle(function(){console.log(new Date().getTime())}, 1000);
  $(window).resize(throttled);
})();

//////////////////////////////////////////////////
// debounce
// _.debounce(function, wait)
//
// デバウンスされる関数を、waitミリ秒経過後まで実行を
// 延期する。入力イベントが終了した後にのみ、処理を
// 実行したい場合に便利。例えば、Markdown記法のプレビューを
// レンダリングする場合や、windowのresizeイベント終了後に
// 処理を実行する際など
//////////////////////////////////////////////////

(function(){
  console.log('### debounce ###');

  var calcLayout = function(){console.log(document.body.clientWidth);}
  var lazyLayout = _.debounce(calcLayout, 300);
  $(window).resize(lazyLayout);
})();

//////////////////////////////////////////////////
// once
// _.once(function)
//
// 一度しか実行されない関数を生成する。functionに変更を
// 加え、繰り返し実行しても何もおこらない。オリジナルの
// 呼び出しが返されるだけである。関数の初期化処理などに
// 便利
//////////////////////////////////////////////////

(function(){
  console.log('### once ###');

  var initialize = _.once(function(){ console.log('init'); });

  initialize();
  initialize();
  // #=> 'init' x 1
})();

//////////////////////////////////////////////////
// after
// _.after(count, function)
//
// count回functionが呼ばれたときにだけ、実行される。
// 非同期処理がすべて終了したときにだけ実行する処理を
// 記述する際に便利。
//////////////////////////////////////////////////

(function(){
  console.log('### after ###');

  var notes = _.range(10);

  var renderNotes = _.after(notes.length, function(note){console.log(note);});
  _.each(notes, function(note){renderNotes(note);});
  // #=> 9
})();

//////////////////////////////////////////////////
// wrap
// _.wrap(function, wrapper)
//
// functionをwrapper関数の中にラップし、第一引数とする。
// これを用いることで、functionの実行前後にwrapperを
// 実行したり、引数を調節したり、条件に応じて実行することが
// できる。
//////////////////////////////////////////////////

(function(){
  console.log('### wrap ###');

  var hello = function(name) { return "hello: "+name; };
  hello = _.wrap(hello, function(func){
    return "before, "+func('hoge')+", after";
  });
  console.log(hello());
  // #=> "before, hello: hoge, after"
})();

//////////////////////////////////////////////////
// compose
// _.compose(*functions)
//
// functionsの合成関数を作る。f(), g(), h()の
// 合成関数は f(g(h()))
//////////////////////////////////////////////////

(function(){
  console.log('### compose ###');

  var greet = function(name) { return "hi: "+ name; }
  var exclaim = function(statement) { return statement + "!"; }
  var welcome = _.compose(exclaim, greet);
  console.log(welcome('hoge'));
  // #=> 'hi: hoge!'
})();

/**
 * Object Functions
 *
 */
////////////////////////////////////////////////////
// keys
// _.keys(object)
//
// objectのプロパティ名を返す
////////////////////////////////////////////////////

(function(){
  console.log(_.keys({one:1, two:2, three:3}));
  // #=> ["one", "two", "three"]
})();

////////////////////////////////////////////////////
// values
// _.values(object)
//
// objectのプロパティ値を返す
////////////////////////////////////////////////////

(function(){
  console.log(_.values({one:1, two:2, three:3}));
  // #=> [1, 2, 3]
})();

////////////////////////////////////////////////////
// functions
// _.funcitons(object) Alias: methods
//
// objectの関数プロパティ名を返す
////////////////////////////////////////////////////
(function(){
  console.log(_.functions(_));
  // #=> ["all", "any", "bind", "bindAll", "clone", "compact", "compose", ...
})();

////////////////////////////////////////////////////
// extend
// _.extend(destination, *sources)
//
// destinationのオブジェクトへ、sourceオブジェクトのプロパティを
// コピーする。コピーは順番で行われ、同名プロパティの場合は、
// 後にコピーしたオブジェクトで上書きされる。
////////////////////////////////////////////////////

(function(){
  console.log(_.extend({a:1, b:2}, {c:3, d:4}));
  // #=> {a:1, b:2, c:3, d:4}
  console.log(_.extend({a:1, b:2}, {c:3, d:4}, {c:4}));
  // #=> {a:1, b:2, c:4, d:4}
})();


////////////////////////////////////////////////////
// defaults
// _.defaults(object, *defaults)
//
// object中に存在しないプロパティをdefaults中のプロパティで
// 埋める。該当プロパティがすでに存在する場合、defaultsの
// 値は適用されない。
////////////////////////////////////////////////////

(function(){
  console.log(_.defaults({a:1, b:2}, {b:3, c:4}));
  // #=> {a:1, b:2, c:4}
  console.log(_.defaults({a:1, b:2}, {b:3, c:4}, {c:5}));
  // #=> {a:1, b:2, c:4}
})();

////////////////////////////////////////////////////
// clone
// _.clone(object)
//
// objectの shallow-copy(浅いコピー）を行う。nestされた
// objectや配列は、参照コピーされる。
////////////////////////////////////////////////////

(function(){
  console.log(_.clone({a:1, b:2}));
  // #=> {a:1, b:2}
})();

////////////////////////////////////////////////////
// tap
// _.tap(object, interceptor)
//
// objectに対して、割り込み実行を行い、object自身を
// 返す。メソッドチェーンの中で割り込み処理を行うのが
// 主目的。
////////////////////////////////////////////////////

(function(){
  var ret = _([1,2,3,200]).chain().
    select(function(num) { return num % 2 == 0; }).
    tap(function(obj){console.log(obj);}).
    map(function(num){ return num * num }).
    value();
  console.log(ret);
  // #=> [2,200]
  // #=> [4, 40000]
})();

////////////////////////////////////////////////////
// isEqual
// _.isEqual(object, other)
//
// 二つのobjectの深い比較を行い、等しいかどうか判定する
////////////////////////////////////////////////////

(function(){
  var obj0 = {name:'ken', nums: [0,1,2]};
  var obj1 = {name:'ken', nums: [0,1,2]};
  console.log(obj0 == obj1);
  // #=> false
  console.log(_.isEqual(obj0, obj1));
  // #=> true
})();

////////////////////////////////////////////////////
// isEmpty
// _.isEmpty(object)
//
// objectが空であれば true を返す。
////////////////////////////////////////////////////

(function(){
  console.log(_.isEmpty({a:1}));
  // #=> false
  console.log(_.isEmpty({}));
  // #=> true
})();

////////////////////////////////////////////////////
// isElement
// _.isElement(object)
//
// objectがDOM要素であれば true を返す
////////////////////////////////////////////////////

(function(){
  console.log(_.isElement($('body')[0]));
  // #=> true
  console.log(_.isElement(0));
  // #=> false
})();

////////////////////////////////////////////////////
// isArray
// _.isArray(object)
//
// objectが配列であれば true を返す
////////////////////////////////////////////////////

(function(){
  console.log(_.isArray([]));
  // #=> true
  console.log(_.isArray({}));
  // #=> false
})();

////////////////////////////////////////////////////
// isArguments
// _.isArguments(object)
//
// objectが引数objectであれば true を返す
////////////////////////////////////////////////////

(function(){
  console.log((function(){ return _.isArguments(arguments);})(1,2,3));
  // #=> true
  console.log(_.isArguments([1,2,3]));
  // #=> false
})();

////////////////////////////////////////////////////
// isFunction
// _.isFunction(object)
//
// objectが関数であれば true を返す
////////////////////////////////////////////////////

(function(){
  console.log(_.isFunction(alert));
  // #=> true
})();

////////////////////////////////////////////////////
// isString
// _.isString(object)
//
// objectが文字列であれば true を返す
////////////////////////////////////////////////////

(function(){
  console.log(_.isString("alert"));
  // #=> true
})();

////////////////////////////////////////////////////
// isNumber
// _.isNumber(object)
//
// objectが数値であれば true を返す
////////////////////////////////////////////////////

(function(){
  console.log(_.isNumber(0));
  // #=> true
})();

////////////////////////////////////////////////////
// isBoolean
// _.isBoolean(object)
//
// objectがBooleanであればtrueを返す
////////////////////////////////////////////////////

(function(){
  console.log(_.isBoolean(true));
  // #=> true
})();

////////////////////////////////////////////////////
// isDate
// _.isDate(object)
//
// objectがDataであればtrueを返す
////////////////////////////////////////////////////

(function(){
  console.log(_.isDate(new Date()));
  // #=> true
})();

////////////////////////////////////////////////////
// isRegExp
// _.isRegExp(object)
//
// objectがRegExpであればtrueを返す
////////////////////////////////////////////////////

(function(){
  console.log(_.isRegExp(/hello/));
  // #=> true
})();

////////////////////////////////////////////////////
// isNaN
// _.isNaN(object)
//
// objectがNaNであればtrueを返す。このメソッドは、ネイティブの
// isNaNとは異なる（ネイティブは undefined でtrueを返すが、
// このメソッドは falseを返す）
////////////////////////////////////////////////////

(function(){
  console.log(_.isNaN(NaN));
  // #=> true
  console.log(isNaN(undefined));
  // #=> true
  console.log(_.isNaN(undefined));
  // #=> false
})();

////////////////////////////////////////////////////
// isNull
// _.isNull(object)
//
// objectがnullであれば、trueを返す。
////////////////////////////////////////////////////

(function(){
  console.log(_.isNull(null));
  // #=> true
  console.log(_.isNull(undefined));
  // #=> true
})();

////////////////////////////////////////////////////
// isUndefined
// _.isUndefined(variable)
//
// variableがundefinedであればtrueを返す。
////////////////////////////////////////////////////

(function(){
  console.log(_.isUndefined(undefined));
  // #=> true
})();


/**
 * Utility Functions
 *
 */
////////////////////////////////////////////////////
// noConflict
// _.noConflict()
//
// 変数 "_" をUnderscore objectから、元に戻す。返値は
// Underscore object
////////////////////////////////////////////////////

// noConflict()を実行すると、以降のsampleが動かなくなるので、
// comment out.
(function(){
  console.log(typeof(_));
  // #=> function
  // var underscore = _.noConflict();
  // console.log(typeof(_));
  // #=> undefined
  // console.log(typeof(underscore));
  // #=> function
})();

////////////////////////////////////////////////////
// identity
// _.identity(value)
//
// 引数と同値を返す。f(x) = x。有用でないように見えるが、
// Underscoreでは、default iteratorとして使われている。
////////////////////////////////////////////////////

(function(){
  console.log(_.identity(1));
  // #=> 1
})();

////////////////////////////////////////////////////
// times
// _.times(n, iterator)
//
// iteratorで与えられた関数を n 回実行する。
////////////////////////////////////////////////////

(function(){
  _.times(3, function(){console.log("1");});
  // #=> 1 が3回表示
})();

////////////////////////////////////////////////////
// mixin
// _.mixin(object)
//
// Underscore objectに独自関数を拡張する。 {name: functions}
// 形式で OOP wrapper のように Underscore objectに関数を
// 追加できる。
////////////////////////////////////////////////////

(function(){
  _.mixin({
    logger: function(str){ console.log(str); }
  });
  _("hello").logger();
  // #=> hello
})();

////////////////////////////////////////////////////
// uniqueId
// _.uniqueId([prefix])
//
// client-sideのモデルや、DOM要素のための グローバル
// ユニークIDを返す。prefixを指定すると、idの接頭辞と
// なる。
////////////////////////////////////////////////////

(function(){
  console.log(_.uniqueId("app_"));
  // #=> app_0
  console.log(_.uniqueId("app_"));
  // #=> app_1
})();

////////////////////////////////////////////////////
// template
// _.template(templateString, [context])
//
// ERB形式の Javascript テンプレートを、レンダリングのための
// 関数にコンパイルする。JSONデータより複雑なHTMLパーツを
// レンダリングするのに有用。<%= ... %>記法で、値を、
// <% ... %>記法で Javascript codeをテンプレート中に挿入
// できる。contextオブジェクトにより、テンプレート中に
// 変数を与えることができる。contextオブジェクトを第二引数
// で与えることで、直接レンダリング結果を得ることもできる。
////////////////////////////////////////////////////

(function(){
  var compiled = _.template("hello: <%= name %>");
  console.log(compiled({name: "moe"}));
  // #=> "hello: moe"
  var list = "<% _.each(people, function(name) { %><li><%= name %></li><% }); %>";
  console.log(_.template(list, {people:['moe', 'curly', 'larry']}));
  // #=> "<li>moe</li><li>curly</li><li>larry</li>"
})();

/////////////////////////////////////////////////////
// Javascriptコードないに print を使うこともできる。<%= ... %>
// よりも有効な場合がある。
/////////////////////////////////////////////////////
(function(){
  console.log(_.template("<% print('Hello '+name); %>", {name: "moe"}));
  // #=> "Hello, moe"
})();


/////////////////////////////////////////////////////
// templateSettingsにオブジェクトを与えることで、
// ERBスタイルを変えることもできる。interpolateプロパティで
// 変数挿入のデリミターを、evaluateプロパティで関数挿入の
// デリミターを指定可能（evaluateプロパティは、オプション）。
// evaluateプロパティが指定されていない場合は、テンプレートは
// 変数挿入のみ可能となる。以下の例は、Mustache.jsの形式に
// 変更する場合。
/////////////////////////////////////////////////////
(function(){
  _.templateSettings = {
    interpolate : /\{\{(.+?)\}\}/g
  }

  console.log(_.template("Hello {{ name }}!", {name: "Mustache"}));
  // #=> "Hello Mustache!"
})();

/**
 * Chaining
 *
 */

/////////////////////////////////////////////////////
// chain
// _(obj).chain()
//
// Underscoreオブジェクトを返す。valueメソッドが呼ばれるまで、
// Underscoreオブジェクトが返されるため、メソッドチェーンが
// 可能となる。
/////////////////////////////////////////////////////
(function(){
  var family = [{name: 'ken', age:39}, {name: 'outa', age: 7}, {name: 'yuyu', age:4}];
  var youngest = _(family).chain()
    .sortBy(function(obj){ return obj.age; })
    .map(function(obj){ return obj.name + " is " + obj.age; })
    .first()
    .value();
  console.log(youngest);
  // #=> "yuyu is 4"
})();

/////////////////////////////////////////////////////
// value
// _.value()
//
// Underscoreオブジェクトから、値を返す
/////////////////////////////////////////////////////
(function(){
  console.log(_([1, 2, 3]).value());
  // #=> [1, 2, 3]
})();
